#include "chessai.h"

chessAi::chessAi()
{
    init_tuple6type();

    qDebug()<<"初始化ai";
}

bool chessAi::checkBound(int x,int y){
    if(x>=0&&x<15&&y>=0&&y<15)return true;
    else return false;
}
QPoint chessAi::getXY(int row, int col, int dir, int rel){
    QPoint p;
    if(dir==RIGHT){
        p.setX(row);
        p.setY(col+rel);
    }else if(dir==UP){
        p.setX(row-rel);
        p.setY(col);
    }else if(dir==UPRIGHT){
        p.setX(row-rel);
        p.setY(col+rel);
    }else if(dir==UPLEFT){
        p.setX(row-rel);
        p.setY(col-rel);
    }
    return p;
}
/**
 * 计算棋子在一个位置的贪婪评分
 *
 * @param board 二维数组，表示当前棋盘状态
 * @param row 行坐标，表示当前考虑的位置的行
 * @param col 列坐标，表示当前考虑的位置的列
 * @param C_ME 标识，表示当前玩家（黑棋或白棋）
 *
 * @return 返回当前位置的贪婪评分
 */
int chessAi::calcOnePosGreedy(int board[15][15], int row, int col, int C_ME) {
    int sum = 0;
    // 遍历四个方向（水平、垂直、两个对角线）
    for (int i = 0; i < 4; ++i) {
        // 每个方向上最多有5个五元组
        for (int j = 0; j < 5; ++j) {
            // 获取当前五元组的顶点位置
            QPoint start = getXY(row, col, RIGHT + i, j - 4);
            // 获取当前五元组的最远端位置
            QPoint end = getXY(start.x(), start.y(), RIGHT + i, 4);
            // 检查五元组的下标是否合法
            if (checkBound(start.x(), start.y()) && checkBound(end.x(), end.y())) {
                int blackChess = 0;
                int whiteChess = 0;
                // 对五元组中的5个位置进行考察
                for (int k = 0; k < 5; ++k) {
                    // 获取五元组中当前位置的坐标
                    QPoint tmp = getXY(start.x(), start.y(), RIGHT + i, k);
                    // 统计黑棋和白棋的数量

                    /*
                        (Chess::find_chess_color(tmp.x(), tmp.y()) == Chessblack) ? blackChess++ : whiteChess++
                    */
                    if (board[tmp.x()][tmp.y()] == C_BLACK) blackChess++;
                    if (board[tmp.x()][tmp.y()] == C_WHITE) whiteChess++;
                }
                // 根据黑棋和白棋的数量计算得分，并累加到总分上
                sum += tupleScoreGreedy(blackChess, whiteChess, C_ME);
            }
        }
    }
    // 返回总分
    return sum;
}

/**
 * @brief 贪心算法评估棋局得分
 *
 * 该函数根据当前棋局中黑棋和白棋的连续情况，以及当前玩家的颜色，
 * 来评估每个棋局的状态价值。连续情况用black和white参数表示，
 * 分别代表黑棋和白棋的最大连续子序列的长度。C_ME参数表示当前玩家的颜色。
 *
 * @param black 黑棋的最大连续子序列长度
 * @param white 白棋的最大连续子序列长度
 * @param C_ME 当前玩家的颜色，C_BLACK表示黑棋，C_WHITE表示白棋
 * @return int 返回当前棋局状态的评估分数
 */
int chessAi::tupleScoreGreedy(int black, int white, int C_ME){
    // 连续五颗同色棋子获胜，返回极大值
    if(C_ME==C_BLACK&&black==5)return 9999999;
    if(C_ME==C_WHITE&&white==5)return 9999999;

    // 如果该位置双方都未占领，则视为潜在可发展区域，给予基础分数
    if(black==0&&white==0)return 7;

    // 如果该位置同时被黑白双方占领，则视为污染区域，分数为0
    else if(black>=1&&white>=1)return 0;
    else if(C_ME==C_BLACK){
        // 以下为黑棋作为当前玩家时，各种连续情况的得分评估
        if(black==1&&white==0)return 35;
        else if(black==2&&white==0)return 800;
        else if(black==3&&white==0)return 15000;
        else if(black==4&&white==0)return 800000;
        // 黑棋防守情况
        else if(black==0&&white==1)return 15;
        else if(black==0&&white==2)return 400;
        else if(black==0&&white==3)return 1800;
        else return 100000;
    }else{
        // 以下为白棋作为当前玩家时，各种连续情况的得分评估
        if(black==1&&white==0)return 15;
        else if(black==2&&white==0)return 400;
        else if(black==3&&white==0)return 1800;
        else if(black==4&&white==0)return 100000;
        // 白棋防守情况
        else if(black==0&&white==1)return 35;
        else if(black==0&&white==2)return 800;
        else if(black==0&&white==3)return 15000;
        else return 800000;
    }
}
/**
 * 贪心算法寻找最佳下棋位置
 *
 * 该函数通过贪心算法遍历棋盘上的每个空位，计算每个空位的分数，
 * 选择分数最高的位置作为最佳移动位置
 *
 * @param C_ME 表示当前玩家的棋子类型
 * @return 返回一个QPoint对象，其中包含最佳移动位置的行列坐标
 */
QPoint chessAi::findBestMoveGreedy(int C_ME){
    // 初始化最佳分数和最佳位置
    int bestScore = 0;
    int bestRow = 0, bestCol = 0;

    // 遍历整个棋盘
    for(int i = 0; i < 15; ++i){
        for(int j = 0; j < 15; ++j){
            // 检查当前位置是否为空
            if(chesses[i][j] == C_NONE){
                // 计算当前位置的分数
                int score = calcOnePosGreedy(chesses, i, j, C_ME);
                // 更新最佳分数和位置
                if(bestScore < score){
                    bestScore = score;
                    bestRow = i;
                    bestCol = j;
                }
            }
        }
    }

    // 创建并返回最佳移动位置的坐标对象
    QPoint p(bestRow, bestCol);
    return p;
}
void chessAi::init_tuple6type(){

    // 初始化 chesses
    for(int i=0;i<15;++i)
        for(int j=0;j<15;++j)
            this->chesses[i][j]=C_NONE;

    memset(tuple6type,0,sizeof (tuple6type));//全部设为0
    //白连5,ai赢
    tuple6type[2][2][2][2][2][2]=WIN;
    tuple6type[2][2][2][2][2][0]=WIN;
    tuple6type[0][2][2][2][2][2]=WIN;
    tuple6type[2][2][2][2][2][1]=WIN;
    tuple6type[1][2][2][2][2][2]=WIN;
    tuple6type[3][2][2][2][2][2]=WIN;//边界考虑
    tuple6type[2][2][2][2][2][3]=WIN;
    //黑连5,ai输
    tuple6type[1][1][1][1][1][1]=LOSE;
    tuple6type[1][1][1][1][1][0]=LOSE;
    tuple6type[0][1][1][1][1][1]=LOSE;
    tuple6type[1][1][1][1][1][2]=LOSE;
    tuple6type[2][1][1][1][1][1]=LOSE;
    tuple6type[3][1][1][1][1][1]=LOSE;
    tuple6type[1][1][1][1][1][3]=LOSE;
    //白活4
    tuple6type[0][2][2][2][2][0]=FLEX4;
    //黑活4
    tuple6type[0][1][1][1][1][0]=flex4;
    //白活3
    tuple6type[0][2][2][2][0][0]=FLEX3;
    tuple6type[0][0][2][2][2][0]=FLEX3;
    tuple6type[0][2][0][2][2][0]=FLEX3;
    tuple6type[0][2][2][0][2][0]=FLEX3;
    //黑活3
    tuple6type[0][1][1][1][0][0]=flex3;
    tuple6type[0][0][1][1][1][0]=flex3;
    tuple6type[0][1][0][1][1][0]=flex3;
    tuple6type[0][1][1][0][1][0]=flex3;
    //白活2
    tuple6type[0][2][2][0][0][0]=FLEX2;
    tuple6type[0][2][0][2][0][0]=FLEX2;
    tuple6type[0][2][0][0][2][0]=FLEX2;
    tuple6type[0][0][2][2][0][0]=FLEX2;
    tuple6type[0][0][2][0][2][0]=FLEX2;
    tuple6type[0][0][0][2][2][0]=FLEX2;
    //黑活2
    tuple6type[0][1][1][0][0][0]=flex2;
    tuple6type[0][1][0][1][0][0]=flex2;
    tuple6type[0][1][0][0][1][0]=flex2;
    tuple6type[0][0][1][1][0][0]=flex2;
    tuple6type[0][0][1][0][1][0]=flex2;
    tuple6type[0][0][0][1][1][0]=flex2;
    //白活1
    tuple6type[0][2][0][0][0][0]=FLEX1;
    tuple6type[0][0][2][0][0][0]=FLEX1;
    tuple6type[0][0][0][2][0][0]=FLEX1;
    tuple6type[0][0][0][0][2][0]=FLEX1;
    //黑活1
    tuple6type[0][1][0][0][0][0]=flex1;
    tuple6type[0][0][1][0][0][0]=flex1;
    tuple6type[0][0][0][1][0][0]=flex1;
    tuple6type[0][0][0][0][1][0]=flex1;

    int p1,p2,p3,p4,p5,p6,x,y,ix,iy;//x:左5中黑个数,y:左5中白个数,ix:右5中黑个数,iy:右5中白个数
    for(p1=0;p1<4;++p1){
        for(p2=0;p2<3;++p2){
            for(p3=0;p3<3;++p3){
                for(p4=0;p4<3;++p4){
                    for(p5=0;p5<3;++p5){
                        for(p6=0;p6<4;++p6){
                            x=y=ix=iy=0;

                            if(p1==1)x++;
                            else if(p1==2)y++;

                            if(p2==1){x++;ix++;}
                            else if(p2==2){y++;iy++;}

                            if(p3==1){x++;ix++;}
                            else if(p3==2){y++;iy++;}

                            if(p4==1){x++;ix++;}
                            else if(p4==2){y++;iy++;}

                            if(p5==1){x++;ix++;}
                            else if(p5==2){y++;iy++;}

                            if(p6==1)ix++;
                            else if(p6==2)iy++;

                            if(p1==3||p6==3){//有边界
                                if(p1==3&&p6!=3){//左边界
                                    //白冲4
                                    if(ix==0&&iy==4){//若右边有空位是活4也没关系，因为活4权重远大于冲4，再加上冲4权重变化可以不计
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=BLOCK4;
                                    }
                                    //黑冲4
                                    if(ix==4&&iy==0){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=block4;
                                    }
                                    //白眠3
                                    if(ix==0&&iy==3){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=BLOCK3;
                                    }
                                    //黑眠3
                                    if(ix==3&&iy==0){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=block3;
                                    }
                                    //白眠2
                                    if(ix==0&&iy==2){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=BLOCK2;
                                    }
                                    //黑眠2
                                    if(ix==2&&iy==0){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=block2;
                                    }
                                }else if(p6==3&&p1!=3){//右边界
                                    //白冲4
                                    if(x==0&&y==4){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=BLOCK4;
                                    }
                                    //黑冲4
                                    if(x==4&&y==0){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=block4;
                                    }
                                    //黑眠3
                                    if(x==3&&y==0){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=BLOCK3;
                                    }
                                    //白眠3
                                    if(x==0&&y==3){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=block3;
                                    }
                                    //黑眠2
                                    if(x==2&&y==0){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=BLOCK2;
                                    }
                                    //白眠2
                                    if(x==0&&y==2){
                                        if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                            tuple6type[p1][p2][p3][p4][p5][p6]=block2;
                                    }
                                }
                            }else{//无边界
                                //白冲4
                                if((x==0&&y==4)||(ix==0&&iy==4)){
                                    if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                        tuple6type[p1][p2][p3][p4][p5][p6]=BLOCK4;
                                }
                                //黑冲4
                                if((x==4&&y==0)||(ix==4&&iy==0)){
                                    if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                        tuple6type[p1][p2][p3][p4][p5][p6]=block4;
                                }
                                //白眠3
                                if((x==0&&y==3)||(ix==0&&iy==3)){
                                    if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                        tuple6type[p1][p2][p3][p4][p5][p6]=BLOCK3;
                                }
                                //黑眠3
                                if((x==3&&y==0)||(ix==3&&iy==0)){
                                    if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                        tuple6type[p1][p2][p3][p4][p5][p6]=block3;
                                }
                                //白眠2
                                if((x==0&&y==2)||(ix==0&&iy==2)){
                                    if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                        tuple6type[p1][p2][p3][p4][p5][p6]=BLOCK2;
                                }
                                //黑眠2
                                if((x==2&&y==0)||(ix==2&&iy==0)){
                                    if(tuple6type[p1][p2][p3][p4][p5][p6]==0)
                                        tuple6type[p1][p2][p3][p4][p5][p6]=block2;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

/**
 * @brief chessAi::evaluate 评估棋盘局面
 * @param board 二维数组，表示当前的棋盘状态
 * @param needPrint 布尔值，表示是否需要打印评估结果
 * @return EVALUATION 返回一个评估对象，包含局面的分数、统计信息和结果
 *
 * 该函数通过分析棋盘上四种方向的棋型来评估当前局面的优劣，
 * 并根据需要打印出评估结果。是实现棋类游戏AI的核心函数之一。
 */
EVALUATION chessAi::evaluate(int board[15][15],bool needPrint){
    // 各棋型权重，正数对AI有利，负数对对手不利
    int weight[17]={0,1000000,-10000000,50000,-100000,400,-100000,400,-8000,20,-50,20,-50,1,-3,1,-3};

    int i, j, type;
    int stat[4][17]; // 统计4个方向上每种棋型的个数
    memset(stat, 0, sizeof(stat));

    int A[17][17]; // 包括边界的虚拟大棋盘, board[i][j]=A[i-1][j-1], 3表示边界
    for(int i=0; i<17; ++i)A[i][0]=3;
    for(int i=0; i<17; ++i)A[i][16]=3;
    for(int j=0; j<17; ++j)A[0][j]=3;
    for(int j=0; j<17; ++j)A[16][j]=3;
    for(int i=0; i<15; ++i)
        for(int j=0; j<15; ++j)
            A[i+1][j+1]=board[i][j];

    // 判断横向棋型
    for(i=1; i<=15; ++i){
        for(j=0; j<12; ++j){
            type=tuple6type[A[i][j]][A[i][j+1]][A[i][j+2]][A[i][j+3]][A[i][j+4]][A[i][j+5]];
            stat[0][type]++;
        }
    }
    // 判断竖向棋型
    for(j=1; j<=15; ++j){
        for(i=0; i<12; ++i){
            type=tuple6type[A[i][j]][A[i+1][j]][A[i+2][j]][A[i+3][j]][A[i+4][j]][A[i+5][j]];
            stat[1][type]++;
        }
    }
    // 判断左上至右下棋型
    for(i=0; i<12; ++i){
        for(j=0; j<12; ++j){
            type=tuple6type[A[i][j]][A[i+1][j+1]][A[i+2][j+2]][A[i+3][j+3]][A[i+4][j+4]][A[i+5][j+5]];
            stat[2][type]++;
        }
    }
    // 判断右上至左下棋型
    for(i=0; i<12; ++i){
        for(j=5; j<17; ++j){
            type=tuple6type[A[i][j]][A[i+1][j-1]][A[i+2][j-2]][A[i+3][j-3]][A[i+4][j-4]][A[i+5][j-5]];
            stat[3][type]++;
        }
    }

    EVALUATION eval;
    memset(eval.STAT, 0, sizeof(eval.STAT));

    int score=0;
    for(i=1; i<17; ++i){
        score+=(stat[0][i]+stat[1][i]+stat[2][i]+stat[3][i])*weight[i]; // 初步计分

        int count=stat[0][i]+stat[1][i]+stat[2][i]+stat[3][i]; // 统计所有方向上部分棋型的个数
        if(i==WIN)eval.STAT[WIN]=count;
        else if(i==LOSE)eval.STAT[LOSE]=count;
        else if(i==FLEX4)eval.STAT[FLEX4]=count;
        else if(i==BLOCK4)eval.STAT[BLOCK4]=count;
        else if(i==FLEX3)eval.STAT[FLEX3]=count;
    }

    /*if(needPrint==true){
        qDebug()<<"局面棋型判断";
        for(int i=0; i<17; ++i)
            qDebug()<<i<<":"<<STAT[i];
    }
*/

    eval.result=R_DRAW;

    // 白赢
    if(eval.STAT[WIN]>0)eval.result=R_WHITE;
    // 黑赢
    else if(eval.STAT[LOSE]>0)eval.result=R_BLACK;

    eval.score=score;
    return eval;
}

void chessAi::show_all_chesses()
{
    for(int i=0;i<15;i++)
    {
        for(int j=0;j<15;j++)
        {
            qDebug()<<"x:"<<i<<" y:"<<j<<" chess:"<<chesses[i][j];
        }
    }
}

void chessAi::run()
{
    qDebug()<<"ai chess";
    // m_chessAi->show_all_chesses();

    this->nodeNum = 0;

    if(!this->analyse_kill(this->chesses,16)){
        qDebug()<<"没找到杀棋";
       this->analyse(this->chesses,6,-INT_MAX,INT_MAX);
    }else{
        qDebug()<<"找到了杀棋";
        kill_user();
    }

    QPoint p=this->decision.pos;

    qDebug()<<"ai落子:"<<p.x()<<","<<p.y();

    this->ai_signals(p.x(), p.y());

    qDebug()<<"ai所求局势得分:"<<this->evaluate(this->chesses).score;

    qDebug()<<"ai叶结点数:"<<this->nodeNum;

    qDebug()<<"ai局面估分:"<<this->evaluate(this->chesses).score;

}


// 定义一个函数，用于寻找棋盘上的最佳落子点
POINTS_AI chessAi::seekPoints(int board[15][15]){
    // 定义一个标记数组，用于记录棋盘上的点是否被考虑过
    bool B[15][15];
    // 定义一个数组，用于记录每个点的价值
    int worth[15][15];
    // 定义一个 POINTS_AI 类型的变量，用于存储最佳落子点信息
    POINTS_AI best_points;

    // 初始化标记数组 B
    memset(B, 0, sizeof(B));
    // 遍历棋盘上的每个点，为每个非空点周围的位置打上标记
    for(int i = 0; i < 15; ++i){
        for(int j = 0; j < 15; ++j){
            if(board[i][j] != C_NONE){
                for(int k = -3; k <= 3; ++k){
                    if(i + k >= 0 && i + k < 15){
                        B[i + k][j] = true;
                        if(j + k >= 0 && j + k < 15) B[i + k][j + k] = true;
                        if(j - k >= 0 && j - k < 15) B[i + k][j - k] = true;
                    }
                    if(j + k >= 0 && j + k < 15) B[i][j + k] = true;
                }
            }
        }
    }

    // 遍历棋盘上的每个点，计算每个可落子位置的价值
    for(int i = 0; i < 15; ++i){
        for(int j = 0; j < 15; ++j){
            worth[i][j] = -INT_MAX;
            // 如果该位置为空且被标记为可走，则计算其价值
            if(board[i][j] == C_NONE && B[i][j] == true){
                // 将该位置临时设为黑棋，以计算其价值
                //board[i][j] = C_BLACK;
                // 使用贪心算法计算该位置的价值
                worth[i][j] = calcOnePosGreedy(board, i, j, C_WHITE);
                // 使用评估函数计算该位置的价值
                //worth[i][j] = evaluate(board).score;
                // 还原该位置的状态
                //board[i][j] = C_NONE;
            }
        }
    }

    // 选取价值最高的20个落子点，并计算其对应的分数
    int w;
    for(int k = 0; k < 20; ++k){
        w = -INT_MAX;
        // 遍历棋盘上的每个点，找到当前价值最高的落子点
        for(int i = 0; i < 15; ++i){
            for(int j = 0; j < 15; ++j){
                if(worth[i][j] > w){
                    w = worth[i][j];
                    // 将该落子点信息存储到最佳落子点结构中
                    QPoint tmp(i, j);
                    best_points.pos[k] = tmp;
                }
            }
        }

        // 获取当前最佳落子点的坐标
        int x = best_points.pos[k].x(), y = best_points.pos[k].y();
        // 将该位置临时设为白棋，以计算其分数
        board[x][y] = C_WHITE;
        // 使用评估函数计算当前最佳落子点的分数，并存储到结构中
        best_points.score[k] = evaluate(board).score;
        // 还原该位置的状态
        board[x][y] = C_NONE;

        // 清除掉已选落子点的价值，以便选择下一个最佳落子点
        worth[best_points.pos[k].x()][best_points.pos[k].y()] = -INT_MAX;
    }
    // 返回最佳落子点信息
    return best_points;
}

/**
 * @brief 复制棋盘
 *
 * 将一个棋盘的状态复制到另一个棋盘。这是通过遍历两个二维数组并根据源棋盘的状态
 * 设置目标棋盘的对应位置来实现的。源棋盘由A表示，目标棋盘由B表示。
 *
 * @param A 源棋盘的指针，表示为一个二维数组
 * @param B 目标棋盘的指针，表示为一个二维数组
 */
void chessAi::copyBoard(int (*A)[15], int (*B)[15]){
    for(int i=0; i<15; ++i){
        for(int j=0; j<15; ++j){
            // 根据源棋盘的格子状态，设置目标棋盘的对应格子状态
            if(A[i][j]==C_NONE) B[i][j]=C_NONE;
            else if(A[i][j]==C_BLACK) B[i][j]=C_BLACK;
            else B[i][j]=C_WHITE;
        }
    }
}

/**
 * @brief 反转棋盘颜色
 *
 * 反转棋盘的颜色，即将黑色变为白色，白色变为黑色，而不改变空格。
 * 这是在两个玩家之间交换颜色时使用的，或者在评估某些对称性时。
 * 棋盘由A表示，反转后的棋盘由B表示。
 *
 * @param A 原始棋盘的指针，表示为一个二维数组
 * @param B 反转后棋盘的指针，表示为一个二维数组
 */
void chessAi::reverseBoard(int (*A)[15], int (*B)[15]){
    for(int i=0; i<15; ++i){
        for(int j=0; j<15; ++j){
            // 根据原始棋盘的格子状态，设置反转后棋盘的对应格子状态
            if(A[i][j]==C_NONE) B[i][j]=C_NONE;
            else if(A[i][j]==C_BLACK) B[i][j]=C_WHITE;
            else B[i][j]=C_BLACK;
        }
    }
}

/**
 * @brief chessAi::analyse 分析函数，用于评估当前棋盘局面的价值
 *
 * 本函数是AI评估棋盘局面的核心算法，采用了Alpha-Beta剪枝算法进行搜索和评估。
 * 它会根据当前的棋盘状态、搜索深度、当前最佳得分（alpha和beta）来决定AI的下一步行动。
 *
 * @param board 当前的棋盘状态，一个15x15的二维数组
 * @param depth 搜索的深度，用于控制递归搜索的层数
 * @param alpha 当前节点的最大得分下限，用于Alpha-Beta剪枝
 * @param beta 当前节点的最小得分上限，用于Alpha-Beta剪枝
 * @return int 返回当前局面的评估分数
 */
int chessAi::analyse(int (*board)[15], int depth,int alpha, int beta){
    // 评估当前棋盘局面
    EVALUATION EVAL = evaluate(board);

    // 如果达到搜索最深层或者局面已分出胜负或和棋，则直接返回结果
    if(depth == 0 || EVAL.result != R_DRAW){
        nodeNum += 1; // 访问的节点数加一
        if(depth == 0){
            // 如果达到最深层，寻找最佳下棋点
            POINTS_AI P;
            P = seekPoints(board);
            return P.score[0]; // 返回最佳位置对应的分数
        }else{
            return EVAL.score; // 返回当前局面的评估分数
        }
    }else if(depth % 2 == 0){
        // 我方（白棋）决策层，寻找最大得分
        POINTS_AI P = seekPoints(board);
        for(int i = 0; i < 10; ++i){
            // 模拟白棋下棋
            int sameBoard[15][15];
            copyBoard(board, sameBoard);
            sameBoard[P.pos[i].x()][P.pos[i].y()] = C_WHITE;
            int a = analyse(sameBoard, depth - 1, alpha, beta);
            if(a > alpha){
                alpha = a;
                if(depth == 6){
                    // 在顶层记录最佳决策
                    qDebug() << "set decision:" << P.pos[i].x() << P.pos[i].y();
                    decision.pos.setX(P.pos[i].x());
                    decision.pos.setY(P.pos[i].y());
                    decision.eval = a;
                }
            }
            if(beta <= alpha) break; // Alpha-Beta剪枝
        }
        return alpha; // 返回当前层的最大得分
    }else{
        // 敌方（黑棋）决策层，寻找最小得分
        int rBoard[15][15];
        reverseBoard(board, rBoard);
        POINTS_AI P = seekPoints(rBoard);
        for(int i = 0; i < 10; ++i){
            // 模拟黑棋下棋
            int sameBoard[15][15];
            copyBoard(board, sameBoard);
            sameBoard[P.pos[i].x()][P.pos[i].y()] = C_BLACK;
            int a = analyse(sameBoard, depth - 1, alpha, beta);
            if(a < beta){
                beta = a;
            }
            if(beta <= alpha) break; // Alpha-Beta剪枝
        }
        return beta; // 返回当前层的最小得分
    }
}

/**
 * 分析棋局并判断是否可以必杀
 * @param board 二维数组，表示当前棋盘状态
 * @param depth 整数，表示搜索的深度
 * @return 返回一个布尔值，表示是否可以必杀
 */
bool chessAi::analyse_kill(int (*board)[15], int depth)
{
    // 评估当前棋局状态
    EVALUATION EVAL = evaluate(board);
    // 如果搜索深度为0或者已经有必胜结果，则结束搜索
    if (depth == 0 || EVAL.result != R_DRAW)
    {
        // 如果抵达最深层
        if (depth == 0)
        {
            // 寻找白棋的最佳下棋位置
            POINTS_AI P;
            P = seekPoints(board);
            board[P.pos[0].x()][P.pos[0].y()] = C_WHITE;

            // 重新评估棋局结果
            gameResult result = evaluate(board).result;
            // 如果白棋胜利，返回true
            if (result == R_WHITE)
                return true;
            else
                return false;
        }
        // 如果找到白棋的必胜局面，返回true
        else if (EVAL.result == R_WHITE)
            return true;
        // 如果白棋输，返回false
        else
            return false;
    }
    // 如果是max层（偶数深度），我方（白棋）决策
    else if (depth % 2 == 0)
    {
        // 在初始几层选择所有能走的10个点
        if (depth == 16 || depth == 14)
        {
            POINTS_AI P = seekPoints(board);
            // 遍历所有可走的位置
            for (int i = 0; i < 10; ++i)
            {
                int sameBoard[15][15];
                copyBoard(board, sameBoard);

                // 模拟白棋落子
                sameBoard[P.pos[i].x()][P.pos[i].y()] = C_WHITE;

                // 如果在这个位置可以必胜，则记录并返回true
                if (analyse_kill(sameBoard, depth - 1))
                {
                    if (depth == 16)
                    {
                        qDebug() << "kill set decision:" << P.pos[i].x() << P.pos[i].y();

                        decision.pos.setX(P.pos[i].x());
                        decision.pos.setY(P.pos[i].y());
                        decision.eval = INT_MAX;
                    }
                    return true;
                }
            }
            return false;
        }
        // 在后续层只选择可能的必杀点
        else
        {
            QList<QPoint> pointList = seek_kill_points(board);

            if (pointList.length() == 0)
                return false;
            // 遍历所有可能的必杀点
            for (auto i : pointList)
            {
                int sameBoard[15][15];
                copyBoard(board, sameBoard);

                // 模拟白棋落子
                sameBoard[i.x()][i.y()] = C_WHITE;

                // 如果在这个位置可以必胜，返回true
                if (analyse_kill(sameBoard, depth - 1))
                {
                    return true;
                }
            }
            return false;
        }
    }
    // 如果是min层（奇数深度），敌方（黑棋）决策
    else
    {
        int rBoard[15][15];
        // 反转棋盘，从黑棋视角评估
        reverseBoard(board, rBoard);
        POINTS_AI P = seekPoints(rBoard);

        int sameBoard[15][15];
        copyBoard(board, sameBoard);
        // 模拟黑棋落子
        sameBoard[P.pos[0].x()][P.pos[0].y()] = C_BLACK;
        // 继续搜索下一层
        return analyse_kill(sameBoard, depth - 1);
    }
}

/**
 * @brief 寻找必杀点
 *
 * 本函数旨在寻找当前棋局中能够立即取胜或形成有利局面的点位。它通过模拟落子并评估棋局变化来实现。
 *
 * @param board 当前棋局状态的二维数组指针
 * @return QList<QPoint> 返回一个包含有利点位的列表，包括能形成连5、活4、冲4或活3的点
 */
QList<QPoint> chessAi::seek_kill_points(int (*board)[15]){
    QList<QPoint> pointList;

    // 寻找潜在的有利点位
    POINTS_AI P = seekPoints(board);

    // 创建一个棋盘副本用于模拟落子
    int sameBoard[15][15];
    copyBoard(board, sameBoard);

    // 遍历前20个潜在的有利点位
    for(int i = 0; i < 20; ++i){
        // 模拟白棋落子
        sameBoard[P.pos[i].x()][P.pos[i].y()] = C_WHITE;
        // 评估模拟落子后的棋局
        if(evaluate(sameBoard).STAT[WIN] > 0){
            // 如果形成连5，则添加该点到列表
            pointList.append(P.pos[i]);
        } else if(evaluate(sameBoard).STAT[FLEX4] > evaluate(board).STAT[FLEX4]){
            // 如果产生新的活4，则添加该点到列表
            pointList.append(P.pos[i]);
        } else if(evaluate(sameBoard).STAT[BLOCK4] > evaluate(board).STAT[BLOCK4]){
            // 如果产生新的冲4，则添加该点到列表
            pointList.append(P.pos[i]);
        } else if(evaluate(sameBoard).STAT[FLEX3] > evaluate(board).STAT[FLEX3]){
            // 如果产生新的活3，则添加该点到列表
            pointList.append(P.pos[i]);
        }
        // 还原落子前的状态
        sameBoard[P.pos[i].x()][P.pos[i].y()] = C_NONE;
    }
    return pointList;
}

